/*************************************************************************
* Copyright(C),2017,ZDC Corporation.
* All right reserved.
* 
* File Name: client.c
* Description:
* Author: madechuan
* Mail: mdc@zcom.nj
* Created Time: 2017年11月02日 星期四 23时59分46秒
************************************************************************/
#include "catch.h"
void tcpclient(const u_char *packet);
/****************抓包回调函数*******************/
void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr, const u_char * packet)
{
    int * id = (int *)arg;
    int i;//只是一个循环变量
    int iplen = (packet[14] & 0x0f) * 4;//ip报头的长度为其第15个字节的低四位X4
    int tcplen = (((packet[(14+iplen+12)]>>4)&0x0f)*4);//tcp报头的长度为tcp头的第13个字节
    int lowlen = 14+iplen+tcplen;//数据链路层，ip层，tcp层所有报头的长度

//在客户端将抓到的报文打印出来测试结果
#if 0
    printf("这个包的长度: %d\n", pkthdr->len); //这个包的长度
    printf("实际抓到的字节数: %d\n", pkthdr->caplen);//实际抓到的字节数

    printf("目的MAC地址:");
    for (i=0; i<6; i++)
    {
        printf(" %02x", packet[i]);
    }
    printf("\n");
    printf("源MAC地址:");
    for (i=6; i<12; i++)
    {
        printf(" %02x", packet[i]);
    }
    printf("\n");
    printf("源IP地址:");
    for (i=26; i<30; i++)
    {
        printf(" %02x", packet[i]);
    }
    printf("\n");
    printf("目的IP地址:");
    for (i=30; i<34; i++)
    {
        printf(" %02x", packet[i]);
    }
    printf("\n");
    printf("IP Header Length is %d\n",iplen);
    printf("TCP报头:");
    printf("Tcp Header Length is %d\n",tcplen);
    for (i=(14+iplen); i<(34+tcplen); i++)
    {
        printf(" %02x", packet[i]);
    }
    printf("\n");
    printf("Http Version:");
    for (i=(lowlen); i<(lowlen+8); i++)
    {
        printf(" %02x", packet[i]);
    }
    printf("\n");
#endif
#if 1
    //http 报文的判别筛选
    unsigned char httpver[8] = {72,84,84,80,47,49,46,49};//http Version字段（HTTP1.1）
    if (0 != memcmp(&packet[lowlen],httpver,8))
    {
        return;//如果不是http，结束回调函数，进行下一次loop
    }
#endif
    printf("已经获取到一个HTTP报文\n");
    tcpclient(packet); //调用发送函数将抓到的报文发到服务器
    printf("\n");
    }


/*********************客户端发送函数（tcp）*********************/
void tcpclient(const u_char *packet)
{
    int tsockfd;    //连接后的套接字
    struct sockaddr_in servaddr;
    char ttransbuf[128];//tcp传输用的buf
    char dstip[64];//用于存放服务器ip地址

    printf("请输入服务器ip地址：");//输入服务器ip地址
    fgets(dstip,32,stdin);

    //建立socket连接
    if (-1 == (tsockfd = socket(AF_INET,SOCK_STREAM,0)))
    {
        perror("socket");
        exit(-1);
    }
    //printf("tlistenfd is %di\n",tlistenfd);
    //设置sockaddr_in结构体中相关的参数
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(atoi("8888"));
    //servaddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    servaddr.sin_addr.s_addr = inet_addr(dstip);
    //printf("going to connect\n");
    //调用connect向服务器请求
    if (-1 == connect(tsockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)))
    {
        perror("connect");
        sleep(2);
        //printf("connect failed\n");
        printf("server not runing\n");
        exit(-1);
    }
    //printf("connect sueeess\n");

    send(tsockfd,packet,128,0);//将抓取到的包buf发送给服务端
    printf(" [ 报文信息已发往服务端... ]\n");
    //判断服务端是否收到消息并给予响应
    int recvflag = 0;//标志位置0
    if (-1 == recv(tsockfd,&recvflag,5,0))
    {
        perror("recv");
        exit(-1);
    }
    if (1 == recvflag)
    {
        printf(" [ 服务端已收到报文信息（服务端响应）]\n");
    }
    else
    {
        printf(" [ 服务端没有收到报文信息（服务端响应）]\n");
    }
    printf("\n");

    //调用recv函数接收发送端发来的数据
    unsigned long systick;//接收系统滴答
    if (-1 == recv(tsockfd,&systick,128,0))
    {
        perror("recv");
        exit(-1);
    }
    printf("服务端发来的系统滴答数：%lu\n",systick);

    recvflag = 1;
    send(tsockfd,&recvflag,5,0);//滴答信息已收到，响应服务端

    close(tsockfd);
    //exit(0);
}
int main()
{
    char errBuf[PCAP_ERRBUF_SIZE], * devStr;

    //获取网络接口设备
    devStr = pcap_lookupdev(errBuf);

    if (devStr)
    {
        printf("Catching Device: %s\n", devStr);
    }
    else
    {
        printf("error: %s\n", errBuf);
        exit(1);
    }

    //打开接口设备，等待抓取到数据包
    pcap_t * device = pcap_open_live(devStr, 1518, 1, 0, errBuf);//设置为最多获取1518个字节

    if (!device)
    {
        printf("error: pcap_open_live(): %s\n", errBuf);
        exit(1);
    }

    //设置抓包过滤器
    struct bpf_program filter;
    pcap_compile(device, &filter, " tcp dst port 80 or tcp src port 80", 1, 0);//过滤tcp协议报文
    pcap_setfilter(device, &filter);

    //手动输入loop次数
#if 0
    printf("pcap_loop函数要循环多少次：");
    //int loopcnt;
    //scanf("%d",&loopcnt);
    //循环抓包，数量可以由pcap_loop的第二个参数指定
#endif
    int id = 0;
    pcap_loop(device, 20, getPacket, (u_char*)&id);//从设备上抓一个包然后结束循环
    pcap_close(device);//释放网络接口

    return 0;
}
